package com.xqboss.apps.service.user;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xqboss.apps.domain.user.*;
import com.xqboss.apps.dto.user.HotBeansGiveDto;
import com.xqboss.apps.enums.user.UserWalletEnum;
import com.xqboss.apps.enums.user.WalletChangeDirectionEnum;
import com.xqboss.apps.enums.user.WalletChangeTypeEnum;
import com.xqboss.apps.enums.user.WalletWrenchChangeTypeEnum;
import com.xqboss.apps.mapper.user.SysUserHotbeansRecordMapper;
import com.xqboss.apps.mapper.user.UserIdentityPermIntegralMapper;
import com.xqboss.apps.mapper.user.UserIdentityRankMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xqboss.apps.vo.user.HotBeansGiveBeforeVo;
import com.xqboss.common.constant.CacheConstants;
import com.xqboss.common.constant.Constants;
import com.xqboss.common.constant.HttpStatus;
import com.xqboss.common.constant.UserConstants;
import com.xqboss.common.core.domain.entity.SysUser;
import com.xqboss.common.core.redis.RedisCache;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.system.mapper.SysUserMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.xqboss.chain.domain.chain.dto.BaseRequest;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 用户热豆转增记录表 服务实现类
 * </p>
 *
 * @author 木鱼
 * @since 2023-12-07
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SysUserHotbeansRecordService extends ServiceImpl<SysUserHotbeansRecordMapper, SysUserHotbeansRecord> {
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private UserIdentityRankMapper userIdentityRankMapper;
    @Autowired
    private UserIdentityPermIntegralMapper userIdentityPermIntegralMapper;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private SysUserWalletService sysUserWalletService;
    @Autowired
    private UserIdentityPermWrenchPresenterService userIdentityPermWrenchPresenterService;
    @Autowired
    private UserAssetWrenchService userAssetWrenchService;
    @Autowired
    private SysUserWrenchRecordService sysUserWrenchRecordService;
    @Autowired
    private UserAssetWrenchRecordService userAssetWrenchRecordService;


    public List<SysUserHotbeansRecord> selectBasicList(BaseRequest request) {
        return baseMapper.selectList(Wrappers.<SysUserHotbeansRecord>lambdaQuery());
    }

    public SysUserHotbeansRecord selectByIdBasic(Long id) {
        return baseMapper.selectById(id);
    }

    public boolean saveBasic(SysUserHotbeansRecord record) {
        return baseMapper.insert(record) > 0;
    }

    public boolean updateBasic(SysUserHotbeansRecord record) {
        return baseMapper.updateById(record) > 0;
    }

    public boolean removeBasic(Long id) {
        return baseMapper.deleteById(id) > 0;
    }


    /**
     * 获取用户身份id
     *
     * @param userId
     * @return
     */
    public Long getUserIdentity(Long userId) {
        SysUser sysUser = sysUserMapper.selectUserById(userId);
        Long identityRankId = sysUser.getIdentityRankId();
        UserIdentityRank userIdentityRank = userIdentityRankMapper.selectById(identityRankId);
        return userIdentityRank.getIdentityId();
    }

    /**
     * 用户转增确认信息
     *
     * @param identityId
     * @return
     */
    public HotBeansGiveBeforeVo hotBeansGiveBefore(Long identityId, Long userId) {
        // 1.查出该用户身份转增配置
        QueryWrapper<UserIdentityPermIntegral> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(UserIdentityPermIntegral::getIdentityId, identityId);
        UserIdentityPermIntegral userIdentityPermIntegral = userIdentityPermIntegralMapper.selectOne(queryWrapper);
        // 组装返回数据
        HotBeansGiveBeforeVo vo = new HotBeansGiveBeforeVo();
        vo.setFeeRate(userIdentityPermIntegral.getFeeRate());
        vo.setMaxDailyTimes(userIdentityPermIntegral.getMaxDailyTimes());
        vo.setMinSingleAmount(userIdentityPermIntegral.getMinSingleAmount());
        vo.setMaxSingleAmount(userIdentityPermIntegral.getMaxSingleAmount());
        // 从redis获取用户已使用热豆转增次数返回,如果没有,则表示用户是今天第一次转增,取数据库配置最大值返回.
        Map<String, Integer> map = redisCache.getCacheMap(CacheConstants.USER_HOT_BEANS_COUNT_ALL_KEY);
        // 热豆使用次数
        Integer integer = map.get(CacheConstants.USER_HOT_BEANS_COUNT_KEY + userId);
        if (integer == null) {// 用户当日没有进行过转增,使用次数应为0
            vo.setHitCount(0);
        } else {
            vo.setHitCount(integer);
        }
        return vo;
    }

    /**
     * 用户热豆转增确认
     *
     * @param dto
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    public void hotBeansGive(HotBeansGiveDto dto, Long userId) {
        BigDecimal amount = dto.getAmount();
        // 获取转增用户每日最大转增次数
        Long identityId = getUserIdentity(userId);
        QueryWrapper<UserIdentityPermIntegral> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(UserIdentityPermIntegral::getIdentityId, identityId);
        UserIdentityPermIntegral userIdentityPermIntegral = userIdentityPermIntegralMapper.selectOne(queryWrapper);
        // 金额安全校验
        checkAmount(dto, amount, userIdentityPermIntegral);

        Integer maxDailyTimes = userIdentityPermIntegral.getMaxDailyTimes();
        // 该身份配置的最大和最小转增数量
        BigDecimal maxSingleAmount = userIdentityPermIntegral.getMaxSingleAmount();
        BigDecimal minSingleAmount = userIdentityPermIntegral.getMinSingleAmount();
        // 被增用户身份判定
        SysUser sysUser = sysUserMapper.selectUserById(dto.getRecipientUserId());
        if (null == sysUser) {
            throw new ServiceException("被赠用户信息异常", HttpStatus.NOT_FOUND);
        }
        // 赠送人钱包
        SysUserWallet giveWallet = sysUserWalletService.getWallet(userId, UserWalletEnum.INTEGRAL);
        // 赠送人余额
        BigDecimal giveWalletAmount = giveWallet.getAmount();
        // 用户余额小于1则不可转增
        if (giveWalletAmount.compareTo(BigDecimal.valueOf(1)) < 0) {
            throw new ServiceException("余额不足1,不可转赠", HttpStatus.Insufficient_Balance);
        }
        BigDecimal realQuantity = dto.getRealQuantity();
        // 余额不足不可转增
        if (giveWalletAmount.compareTo(realQuantity) < 0) {
            throw new ServiceException("余额不足,不可转赠", HttpStatus.Insufficient_Balance);
        }
        // 赠送数量不符合该身份配置不可转增
        if (amount.compareTo(maxSingleAmount) > 0 || amount.compareTo(minSingleAmount) < 0) {
            throw new ServiceException("赠送数量不符合该身份最大或最小赠送量");
        }
        // 添加热豆转增记录表
        Long sourceId = setHotBeansLog(dto, userId);

        // 更新被赠人钱包
        sysUserWalletService.increase(dto.getRecipientUserId(), UserWalletEnum.INTEGRAL, amount, WalletChangeTypeEnum.HOT_GIVE, sourceId, WalletChangeTypeEnum.HOT_GIVE.getMsg());
        // 更新赠送人钱包
        sysUserWalletService.reduce(userId, UserWalletEnum.INTEGRAL, dto.getRealQuantity(), WalletChangeTypeEnum.HOT_GIVE, sourceId, WalletChangeTypeEnum.HOT_GIVE.getMsg());
        // 缓存更新
        updateCache(userId, maxDailyTimes);
    }


    /**
     * 扳手转赠确认
     *
     * @param dto
     * @param userId
     */
    @Transactional(rollbackFor = Exception.class)
    public void wrenchGive(HotBeansGiveDto dto, Long userId) {
        BigDecimal amount = dto.getAmount();
        // 获取转增用户每日最大转增次数
        Long identityId = getUserIdentity(userId);
        UserIdentityPermWrenchPresenter wrenchPresenter = userIdentityPermWrenchPresenterService.lambdaQuery()
                .eq(UserIdentityPermWrenchPresenter::getIdentityId, identityId)
                .one();
        // 扳手赠送金额校验
        checkAmount(dto, amount, wrenchPresenter);
        // 每日最大次数
        Integer maxDailyTimes = wrenchPresenter.getMaxDailyTimes();
        // 该身份配置的最大和最小转增数量
        BigDecimal maxSingleAmount = wrenchPresenter.getMaxSingleAmount();
        BigDecimal minSingleAmount = wrenchPresenter.getMinSingleAmount();
        // 被增用户身份判定
        SysUser sysUser = sysUserMapper.selectUserById(dto.getRecipientUserId());
        if (null == sysUser) {
            throw new ServiceException("被赠用户信息异常");
        }
        // 赠送人扳手钱包
        UserAssetWrench giveWrenchWallet = userAssetWrenchService.getByUserId(userId);
        // 赠送人余额
        BigDecimal giveWalletAmount = giveWrenchWallet.getAmount();
        // 用户余额小于1则不可转增
        if (giveWalletAmount.compareTo(BigDecimal.valueOf(1)) < 0) {
            throw new ServiceException("余额不足1,不可转赠", HttpStatus.Insufficient_Balance);
        }
        BigDecimal realQuantity = dto.getRealQuantity();
        // 余额不足不可转增
        if (giveWalletAmount.compareTo(realQuantity) < 0) {
            throw new ServiceException("余额不足,不可转赠", HttpStatus.Insufficient_Balance);
        }
        // 赠送数量不符合该身份配置不可转增
        if (amount.compareTo(maxSingleAmount) > 0 || amount.compareTo(minSingleAmount) < 0) {
            throw new ServiceException("赠送数量不符合该身份最大或最小赠送量");
        }
        // 添加记录
        Long sourceId = insertRecord(dto, userId);
        // 更新被赠人扳手余额
        userAssetWrenchRecordService.updateWrenchBalance(dto.getRecipientUserId(), WalletWrenchChangeTypeEnum.WRENCH_GIVE, WalletChangeDirectionEnum.IN, amount, sourceId, "扳手转赠");
        // 更新赠送人扳手余额
        userAssetWrenchRecordService.updateWrenchBalance(userId, WalletWrenchChangeTypeEnum.WRENCH_GIVE, WalletChangeDirectionEnum.OUT, dto.getRealQuantity(), sourceId, "扳手转赠");
        // 缓存更新
        wrenchCacheUpdate(userId, maxDailyTimes);
    }

    /**
     * 扳手转赠缓存更新
     * @param userId
     * @param maxDailyTimes
     */
    private void wrenchCacheUpdate(Long userId, Integer maxDailyTimes) {
        Integer integer = redisCache.getCacheMapValue(CacheConstants.USER_WRENCH_COUNT_ALL_KEY, CacheConstants.USER_WRENCH_COUNT_KEY + userId);
        Integer defaultInteger = UserConstants.DEFAULT_COUNT;
        if (null == integer) {// 没有该用户记录
            redisCache.setCacheMapValue(CacheConstants.USER_WRENCH_COUNT_ALL_KEY, CacheConstants.USER_WRENCH_COUNT_KEY + userId, defaultInteger);
        } else {// 有该用户记录
            // 判断用户是否还有转增次数
            if (integer >= maxDailyTimes) {
                throw new ServiceException("转赠次数不足,请明天再试", HttpStatus.insufficient_give_hot_beans);
            }
            redisCache.setCacheMapValue(CacheConstants.USER_WRENCH_COUNT_ALL_KEY, CacheConstants.USER_WRENCH_COUNT_KEY + userId, integer + 1);
        }
    }

    /**
     * 添加扳手转赠记录
     *
     * @param dto
     * @param userId
     */
    private Long insertRecord(HotBeansGiveDto dto, Long userId) {
        SysUserWrenchRecord wrenchRecord = new SysUserWrenchRecord();
        wrenchRecord.setGiveUserId(userId);
        wrenchRecord.setRecipientUserId(dto.getRecipientUserId());
        wrenchRecord.setAmount(dto.getAmount());
        wrenchRecord.setServiceCharge(dto.getServiceCharge());
        sysUserWrenchRecordService.save(wrenchRecord);
        return wrenchRecord.getId();
    }

    /**
     * 扳手转赠金额校验
     *
     * @param dto
     * @param amount
     * @param wrenchPresenter
     */
    private void checkAmount(HotBeansGiveDto dto, BigDecimal amount, UserIdentityPermWrenchPresenter wrenchPresenter) {
        // 计算手续费
        BigDecimal decimal = new BigDecimal(wrenchPresenter.getFeeRate()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
        // 手续费校验
        BigDecimal setScale = amount.multiply(decimal).setScale(2, RoundingMode.HALF_UP);
        if (setScale.compareTo(dto.getServiceCharge()) != 0) {
            throw new ServiceException("服务费有误,转赠失败");
        }
        // 实际支付校验
        BigDecimal add = amount.add(setScale).setScale(2, RoundingMode.HALF_UP);
        if (add.compareTo(dto.getRealQuantity()) != 0) {
            throw new ServiceException("支付费用有误,转赠失败");
        }
    }

    /**
     * 金额安全校验
     *
     * @param dto
     * @param amount
     * @param userIdentityPermIntegral
     */
    private void checkAmount(HotBeansGiveDto dto, BigDecimal amount, UserIdentityPermIntegral userIdentityPermIntegral) {
        // 计算手续费
        BigDecimal decimal = new BigDecimal(userIdentityPermIntegral.getFeeRate()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
        // 手续费校验
        BigDecimal setScale = amount.multiply(decimal).setScale(2, RoundingMode.HALF_UP);
        if (setScale.compareTo(dto.getServiceCharge()) != 0) {
            throw new ServiceException("服务费有误,转赠失败");
        }

        // 实际支付校验
        BigDecimal add = amount.add(setScale).setScale(2, RoundingMode.HALF_UP);
        if (add.compareTo(dto.getRealQuantity()) != 0) {
            throw new ServiceException("支付费用有误,转赠失败");
        }
    }

    /**
     * 缓存更新
     *
     * @param userId
     * @param maxDailyTimes
     */
    private void updateCache(Long userId, Integer maxDailyTimes) {
        Integer integer = redisCache.getCacheMapValue(CacheConstants.USER_HOT_BEANS_COUNT_ALL_KEY, CacheConstants.USER_HOT_BEANS_COUNT_KEY + userId);
        Integer defaultInteger = UserConstants.DEFAULT_COUNT;
        if (null == integer) {// 没有该用户记录
            redisCache.setCacheMapValue(CacheConstants.USER_HOT_BEANS_COUNT_ALL_KEY, CacheConstants.USER_HOT_BEANS_COUNT_KEY + userId, defaultInteger);
        } else {// 有该用户记录
            // 判断用户是否还有转增次数
            if (integer.intValue() >= maxDailyTimes.intValue()) {
                throw new ServiceException("转赠次数不足,请明天再试", HttpStatus.insufficient_give_hot_beans);
            }
            redisCache.setCacheMapValue(CacheConstants.USER_HOT_BEANS_COUNT_ALL_KEY, CacheConstants.USER_HOT_BEANS_COUNT_KEY + userId, integer + 1);
        }
    }

    // 保存热豆变动记录表
    private Long setHotBeansLog(HotBeansGiveDto dto, Long userId) {
        SysUserHotbeansRecord userHotbeansRecord = new SysUserHotbeansRecord();
        userHotbeansRecord.setGiveUserId(userId);
        userHotbeansRecord.setRecipientUserId(dto.getRecipientUserId());
        userHotbeansRecord.setAmount(dto.getAmount());
        userHotbeansRecord.setCreateTime(new Date());
        userHotbeansRecord.setServiceCharge(dto.getServiceCharge());
        userHotbeansRecord.setIsDel(false);
        this.save(userHotbeansRecord);
        Long sourceId = userHotbeansRecord.getId();
        return sourceId;
    }


}